home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 1.iso / dist / fw_exmh.idb / usr / freeware / lib / exmh-2.5 / editor.tcl.z / editor.tcl
Text File  |  2002-07-08  |  17KB  |  568 lines

  1. #
  2. # editor.tcl --
  3. #    Editor interactions
  4. #
  5. # Copyright (c) 1993 Xerox Corporation.
  6. # Use and copying of this software and preparation of derivative works based
  7. # upon this software are permitted. Any distribution of this software or
  8. # derivative works must comply with all applicable United States export
  9. # control laws. This software is made available AS IS, and Xerox Corporation
  10. # makes no warranty about the software, its performance or its conformity to
  11. # any specification.
  12.  
  13. # Message composing and editor interaction
  14.  
  15. proc Edit_Init {} {
  16.     global mhProfile editor
  17.     case $mhProfile(editor) {
  18.     {*mxedit* sedit} {set prog $mhProfile(editor)}
  19.     default {set prog sedit}
  20.     }
  21.     Preferences_Add "Editor Support" \
  22. "Exmh has a built-in editor for composing messages, named \"sedit\".
  23. It can also interface to other editors.  For non-TK editors like vi
  24. or emacs, preface the editor command with \"exmh-async\" so the editor
  25. runs as a detached process.  For TK-aware applications, suffix the
  26. command with an & so your editor detaches into the background.  In
  27. this case the first argument to the editor is the name of the exmh
  28. interpreter, and the second argument is the draft file name.
  29. If your editor knows how to post a message directly, preface
  30. the command with \"give-away\".  The emacsclient and gnuclient
  31. cases listed in the examples below are special cases of this.
  32. If you want to pass a -geo argument to your program, you need to
  33. wrap it up to prevent exmh-async from grabbing it.  See the last
  34. example below.
  35. Example commands include:
  36. sedit
  37. mxedit
  38. emacsclient &
  39. give-away emacsclient
  40. gnuclient &
  41. give-away gnuclient
  42. exmh-async emacs
  43. exmh-async emacsclient
  44. exmh-async xterm -e vi
  45. exmh-async xterm {-geom 80x44+0+0} -e vi" \
  46.     [list \
  47.     [list editor(prog) editCmd $prog {Editor command} \
  48. "The editor command used to compose mail messages.
  49. The filename will be appended to the command."] \
  50.     [list editor(alternate) edit2Cmd {exmh-async emacs} {2nd Editor command} \
  51. "This is an alternate editor command.  For example, if your default
  52. editor is sedit, you might want to drop into emacs for a particular
  53. message.  The filename will be appended to the command."] \
  54.     [list editor(autowhom) autoWhom ON {Auto Whom} \
  55. "Enabling this causes whom to be invoked automatically whenever a
  56. What Now? dialog is displayed."] \
  57.     [list editor(sedit!) seditOverride OFF {Sedit override} \
  58. "Use this to override the regular editor command and
  59. use the built-in editor instead.  This parameter is set
  60. initially by the command line -sedit switch."] \
  61.     [list editor(async_sedit) seditAfterAsync OFF {Use Sedit after Async} \
  62. "If enabled, after editing with external editor, exmh will bring the message
  63. up in sedit.  This is so you can initially create the message with your
  64. favorite editor, and then pop into to sedit for richtext or attachments."] \
  65.     [list editor(spell) spellCmd "exmh-async xterm -e ispell" {Spell command} \
  66. "The spell command specifies a program used to spell-check mail messages. 
  67. The filename will be appended to the command."] \
  68.         [list editor(mhn) mhnCmd "mhn" {MHN command} \
  69. "The mhn command specifies a program used to reformat
  70. a message with the MH mhn program.
  71. The filename will be appended to the command."] \
  72.     ]
  73.     set editor(sedit) sedit
  74. }
  75.  
  76. proc Edit_Draft {} {
  77.     global mhProfile exmh
  78.     # Run the editor on the current draft
  79.     if {$exmh(folder) == $mhProfile(draft-folder)} {
  80.     Msg_CheckPoint    ;# Update cur message
  81.     }
  82.     set draftID [Mh_Cur $mhProfile(draft-folder)]
  83.     if {$draftID == {}} {
  84.     Exmh_Status "No current message in Draft-Folder $mhProfile(draft-folder)" error
  85.     return
  86.     }
  87.     EditWhatNow $draftID prog
  88. }
  89. proc Edit_DraftID { id } {
  90.     global exmh
  91.     # No mucking with context. We normally get here by-passing the
  92.     # standard draft creation process. Hence we need to set exmh($id,action)
  93.     set exmh($id,action) auto
  94.     EditWhatNow $id prog
  95. }
  96.  
  97. proc EditDraftFolderHook { f what } {
  98.     # Called when we change to the drafts folder
  99.     Buttons_DraftMode 1
  100. }
  101. proc EditDraftFolderCleanup { f what } {
  102.     Buttons_DraftMode 0
  103. }
  104.  
  105. # Throw up a dialog asking whether user wants to
  106. # send, re-edit, save as draft, abort.
  107. # As with the command line whatnowproc, this also starts up the
  108. # editor on the draft.
  109.  
  110. proc EditWhatNow {draftID {edittype prog}} {
  111.     global mhProfile editor
  112.  
  113.     if {([string compare $edittype prog] == 0) && $editor(sedit!)} {
  114.     set edittype sedit    ;# sedit override
  115.     }
  116.     Exmh_Debug EditWhatNow $draftID $edittype
  117.     if ![regexp {^[0-9]+$} $draftID] {
  118.     # Must be a message in a non-drafts folder
  119.     set path $mhProfile(path)/$draftID
  120.     } else {
  121.     # Delete ".orig" file if it exists, Mhn_* thanks to Colm Buckley
  122.     Mhn_DeleteOrig $draftID
  123.     set path [Mh_Path $mhProfile(draft-folder) $draftID]
  124.     }
  125.     if [EditStart $path $edittype] {
  126.     # Editor has run synchronously, so we go right into the dialog
  127.     Edit_Dialog $draftID
  128.     }
  129.     global exmh
  130.     if {[string compare $exmh(folder) $mhProfile(draft-folder)] == 0} {
  131.     # It isn't safe to leave a current message active in the drafts
  132.     # folder because a second edit can loose changes in the first.
  133.     # User saves a draft.
  134.     # User changes to draft folder.
  135.     # The "Send" button uses the saved draft, and they make edits.
  136.     # Another "Send" button hit resets the draft to their last save,
  137.     # but they probably expected to start a new draft.
  138.     Msg_ClearCurrent
  139.     }
  140. }
  141.  
  142. # The following can be invoked by remote editor interpreters
  143. proc Edit_Dialog {draftID} {
  144.     global exmh mhProfile editor
  145.     Exmh_Debug Edit_Dialog $draftID
  146.     if {$editor(async_sedit)} {
  147.     Sedit_Start $mhProfile(path)/$mhProfile(draft-folder)/$draftID
  148.     } else {
  149.     EditShowDialog $draftID "What should I do with draft $draftID?"
  150.     }
  151. }
  152. # For compatibility with old versions of mxedit
  153. proc EditDialog {draftID} {
  154.     Edit_Dialog $draftID
  155. }
  156.  
  157. # Turn passphrase pane ON/OFF/disabled depending on pgp(seditpgp) and
  158. # pgp(keeppass)
  159. proc EditMaybeAddPhrasePane {id w} {
  160.     global pgp
  161.  
  162.     if {!$pgp(enabled)} {
  163.     return
  164.     }
  165.  
  166.     set dismsg "Disabled since passphrases are not being kept."
  167.     if {$pgp(seditpgp)} {
  168.     # Need to add pane
  169.     if {[lsearch [pack slaves $w] $w.pgp] < 0} {
  170.         EditAddPassPhrasePane $id $w
  171.     }
  172.     # Just in case the user is playing with pgp(keeppass) value
  173.     if {$pgp(echopass)} {
  174.         $w.pgp.e configure -show * -state normal
  175.     } else {
  176.         $w.pgp.e configure -show {} -state normal
  177.     }
  178.     if {![string compare $pgp(cur,pass,$id) $dismsg]} {
  179.         set pgp(cur,pass,$id) {}
  180.     }
  181.     } else {
  182.     # Need to take away the pane (if it is there)
  183.     if {[lsearch [pack slaves $w] $w.pgp] >= 0} {
  184.         destroy $w.pgp
  185.     }
  186.     }
  187.  
  188.     if {!$pgp(keeppass) && [lsearch [pack slaves $w] $w.pgp] >= 0} {
  189.     # Need to disable the pane (if it is there)
  190.     set pgp(cur,pass,$id) $dismsg
  191.     $w.pgp.e configure -show {} -state disabled
  192.     }
  193. }
  194.  
  195. proc EditAddPassPhrasePane {id w} {
  196.     global pgp
  197.     if {$pgp(enabled) && $pgp(seditpgp)} {
  198.  
  199.     # seditpgp main area
  200.     Pgp_SetSeditPgpName $pgp($pgp(version,$id),myname,$id) $id
  201.     set pgp(fullName,$id) $pgp($pgp(version,$id),fullName)
  202.  
  203.     if {![winfo exists $w.pgp]} {
  204.         pack [frame $w.pgp] -side bottom -fill x -ipady 2
  205.     }
  206.     if {![winfo exists $w.pgp.l1]} {
  207.         pack [label $w.pgp.l1 -text "Passphrase for "] \
  208.             -side left
  209.     }
  210.     if {![winfo exists $w.pgp.b]} {
  211.         pack [button $w.pgp.b -textvariable pgp(sedit_label,$id) \
  212.             -command "Pgp_SetMyName \$pgp(version,$id) $id"] \
  213.             -side left -ipady 2
  214.     }
  215.     if {![winfo exists $w.pgp.l2]} {
  216.         pack [label $w.pgp.l2 -text ": "] \
  217.             -side left
  218.     }
  219.     if {![winfo exists $w.pgp.e]} {
  220.         pack [entry $w.pgp.e -textvariable pgp(cur,pass,$id)] \
  221.             -side left -expand yes -fill x -ipady 2
  222.         if {$pgp(echopass)} {
  223.         $w.pgp.e configure -show *
  224.         }
  225.     }
  226.  
  227.     # Add extras if requested
  228.     if {$pgp(seditpgpextras)} {
  229.         if {![winfo exists $w.pgp2]} {
  230.         pack [frame $w.pgp2] -side bottom -fill x
  231.         }
  232.         if {![winfo exists $w.pgp2.l1]} {
  233.         pack [label $w.pgp2.l1 -textvariable pgp(sedit_label2,$id)] -side left
  234.         }
  235.     }
  236.     }
  237. }
  238. proc EditShowDialog {id text} {
  239.     global exwin editor pgp
  240.     # Create the buttons and arrange them from left to right in the RH
  241.     # frame. Embed the left button in an additional sunken frame to indicate
  242.     # that it is the default button.
  243.  
  244.     if [Exwin_Toplevel .edit$id "What Now?" WhatNow nomenu] {
  245.     set d .edit$id
  246.     wm transient $d
  247.     $d config -relief raised -borderwidth 2
  248.  
  249.     # PGP version-setting moved out from seditpgp code
  250.     if {$pgp(enabled)} {
  251.         if {![info exists pgp($pgp(version,$id),myname,$id)]} {
  252.         set pgp($pgp(version,$id),myname,$id) $pgp($pgp(version,$id),myname)
  253.         }
  254.         EditMaybeAddPhrasePane $id $d
  255.     }
  256.  
  257.     foreach but [Widget_GetButDef $d] {
  258.         Widget_AddButDef $d $but {right}
  259.         Widget_ReEvalCmd $d.$but    ;# expand $id variable now
  260.     }
  261.     catch {pack $d.abort -padx 15}
  262.     catch {pack $d.send -ipady 5 -ipadx 5 -padx 5}
  263.  
  264.     foreach M [Widget_GetMenuBDef $d] {
  265.         set menu [Widget_AddMenuBDef $d $M {right padx 1}]
  266.         ButtonMenuInner $menu    ;# This also expands variables
  267.     }
  268.  
  269.     # Make sure only valid entries are enabled in version submenu
  270.     # otherwise things may fail in post-processing.
  271.     # We don't have to take care of the active entry since 
  272.     # preferences only allows valid entries as initial version
  273.     if {$pgp(enabled)} {
  274.         set submenu $d.more.m.encrypt.version
  275.         for {set sm 0} {$sm<=[$submenu index last]} {incr sm} {
  276.         if [catch {$submenu entrycget $sm -value} v] {
  277.             continue
  278.         }
  279.         if {[info exists pgp($v,enabled)] && \
  280.             !$pgp($v,enabled) } {
  281.             $submenu entryconfigure $sm -state disabled
  282.         }
  283.         }
  284.     }
  285.  
  286.     Widget_Message $d msg -text "$text\nReturn for Send\nControl-c for Kill" -aspect 400
  287.  
  288.     focus $d
  289.     bind $d <Return> [list $d.send invoke]
  290.     bind $d <Control-c> [list $d.abort invoke]
  291.  
  292.     Visibility_Wait .edit$id
  293.     Exmh_Focus
  294.     } else {
  295.     set d .edit$id
  296.         catch {destroy $d.f}    ;# Whom results
  297.     EditMaybeAddPhrasePane $id $d
  298.     }
  299.     if $editor(autowhom) {
  300.     EditDialogDone whom $id nohide
  301.     }
  302. }
  303. proc EditDialogMsg {id msg} {
  304.     set d .edit$id
  305.     catch {destroy $d.f}
  306.     Widget_Frame $d f
  307.     pack $d.f -before [lindex [pack slaves $d] 0] -side bottom
  308.     set lines [llength [split $msg \n]]
  309.     Widget_Text $d.f [expr {$lines > 8 ? 8 : $lines}]
  310.     $d.f.t insert 1.0 $msg
  311.     $d.f.t config -state disabled -width 40
  312. }
  313. proc EditDialogDone {act id {hide hide}} {
  314.     if [string match hide $hide] {
  315.     Exwin_Dismiss .edit$id nosize
  316.     }
  317.     Edit_Done $act $id
  318. }
  319.  
  320. proc EditStart { draft {type prog} } {
  321.     # Start the editor, reusing an existing session if possible
  322.     global editor exmh mhProfile pgp
  323.  
  324.     Exmh_Debug EditStart $draft $type
  325.  
  326.     if $pgp(enabled) {
  327.     # Copy the default PGP values into this window only if they
  328.     # don't already exists. This way, we preserve values between
  329.     # re-edit sessions. Edit_Done takes care of resetting to 
  330.     # preference values when we send or abort (ie. get done draft).
  331.     set id [SeditId $draft]
  332.     foreach var {encrypt sign format version} {
  333.         if ![info exists pgp($var,$id)] {
  334.         set pgp($var,$id) $pgp($var)
  335.         }
  336.     }
  337.     }
  338.     
  339.     switch -glob -- $editor($type) {
  340.     *mxedit* {
  341.         if ![info exists exmh(editInterp)] {
  342.         set exmh(editInterp) "mxedit"
  343.         }
  344.         if ![info exists exmh(mxeditVersion)] {
  345.         if ![catch {send $exmh(editInterp) {set mxVersion}} vers] {
  346.             set exmh(mxeditVersion) $vers
  347.         }
  348.         }
  349.         set id "$exmh(editInterp) $draft"
  350.         if [info exists exmh(mxeditVersion)] {
  351.         if {$exmh(mxeditVersion) >= 2.4} {
  352.             global env
  353.             if [regsub $env(HOME) $draft ~ newpath] {
  354.             set id "$exmh(editInterp) $newpath"
  355.             }
  356.         }
  357.         }
  358.         Exmh_Debug $id mxReset
  359.         if [catch {send $id mxReset}] {
  360.         if [catch {send $exmh(editInterp) {set mxVersion}}] {
  361.             Exmh_Status "Starting mxedit..." warn
  362.             # Start the editor and tell it to make a callback
  363.             # that identifies the TCL interpreter in the editor
  364.             eval exec $editor($type) {-globalCmd [list mxSendInterpName [winfo name .] Edit_Ident] $draft &}
  365.         } else {
  366.             Exmh_Status "Opening mxedit..." warn
  367.             catch {send $exmh(editInterp) [list mxOpen $draft]}
  368.         }
  369.         } else {
  370.         Exmh_Status "Reopening mxedit..." warn
  371.         catch {send $id {wm deiconify .}}
  372.         }
  373.         return 0        ;# Asynchronous edit
  374.     }
  375.     sedit {
  376.         Sedit_Start $draft
  377.         return 0        ;# Asynchronous edit
  378.     }
  379.     exmh-async* {
  380.         global wish argv0
  381.         Exmh_Status "Starting ASYNC $editor($type) ..." warn
  382.         eval {exec $wish -f ${argv0}-async -- [winfo name .]} \
  383.         [lrange $editor($type) 1 end] {$draft &}
  384.         return 0        ;# Asynchronous edit
  385.     }
  386.     give-away* -
  387.     gnuclient*& -
  388.     emacsclient*& {
  389.         set cmd $editor($type)    ;# Tcl 7.0 bug in regsub
  390.         regsub ^give-away $cmd {} cmd
  391.             set cmd [string trimright $cmd "& \t"]
  392.         Exmh_Status "Starting $cmd ..." warn
  393.             if [catch {eval exec $cmd $draft &} err] {
  394.                 Exmh_Status $err error
  395.             }
  396.         return 0        ;# Asynchronous edit
  397.  
  398.     }
  399.     *& {
  400.         Exmh_Status "Starting TK $editor($type) ..." warn
  401.             set cmd [string trimright $editor($type) "& \t"]
  402.             if [catch {eval exec $cmd \"[winfo name .]\" $draft &} err] {
  403.                 Exmh_Status $err error
  404.             }
  405.         return 0        ;# Asynchronous edit
  406.  
  407.     }
  408.     default {
  409.         Exmh_Status "Starting $editor($type) ..." warn
  410.         if [catch {eval exec $editor($type) $draft} err] {
  411.         Exmh_Status $err error
  412.         }
  413.         return 1        ;# Synchronous edit
  414.     }
  415.     }
  416. }
  417.  
  418. # The following is invoked via "send" by mxedit when it
  419. # starts up the first time in order to identify itself to us.
  420.  
  421. proc Edit_Ident { interpName } {
  422.     global exmh
  423.     set exmh(editInterp) $interpName
  424. }
  425.  
  426. # The following is invoked by remote editor interpreters
  427. proc EditDone {act msg} {
  428.     Edit_Done $act $msg
  429. }
  430. proc Edit_Done {act {msg cur}} {
  431.     # Commit or abort an edit session
  432.     global mhProfile exmh env editor pgp
  433.     if {$msg == "cur"} {
  434.     set msg [Mh_Cur $mhProfile(draft-folder)]
  435.     if {$msg == {}} {
  436.         Exmh_Status "No current draft"
  437.         return
  438.     }
  439.     }
  440.     Exmh_Debug action = $act msg = $msg
  441.     if ![regexp {^[0-9]+$} $msg] {
  442.     # Message not in the drafts folder
  443.     set path $mhProfile(path)/$msg
  444.     }
  445.     case $act in {
  446.     send    {
  447.         Aliases_CheckPoint
  448.         if [info exists path] {
  449.         # Copy message to drafts folder
  450.         set id [file tail [Mh_Path $mhProfile(draft-folder) new]]
  451.         Exmh_Status "Copying $msg to draft $id"
  452.         MhExec comp +[file dirname $msg] [file tail $msg] -nowhatnowproc
  453.         set msg $id
  454.         }
  455.         set anno [Mh_AnnoEnviron $msg]
  456.         Exmh_Debug Edit_Done send: anno=$anno
  457.         Exmh_Debug "Mh_Send [time {set code [catch {Mh_Send $msg} err2]}]"
  458.         if $code {
  459.         # move the orig message back to the draft if it exists
  460.         Mhn_RenameOrig $msg
  461.         Exmh_Status $err2 error
  462.         Send_Error $err2 $msg
  463.         return
  464.         }
  465.         if {$exmh(folder) == $mhProfile(draft-folder)} {
  466.         # Clean up scan listing
  467.         if [catch {Msg_RemoveById $msg} err] {
  468.             Exmh_Debug Msg_RemoveById $msg $err
  469.         }
  470.         }
  471.         Exmh_Status "Draft $msg sent" normal
  472.         # Delete "orig" message if it exists
  473.         Mhn_DeleteOrig $msg
  474.         Quote_Cleanup
  475.         if {$anno} {
  476.         if {[string compare $exmh($msg,folder) $exmh(folder)] == 0} {
  477.             set ix [Ftoc_FindMsg $exmh($msg,mhmessages)]
  478.             Exmh_Debug Edit_Done ix=$ix
  479.             if {$ix != {}} {
  480.             Ftoc_RescanLine $ix dash
  481.             }
  482.         }
  483.         }
  484.         Mh_AnnoCleanup $msg
  485.         if $pgp(enabled) {
  486.         # Done with this draft, set PGP defaults for next call
  487.         set id [SeditId $msg]
  488.         foreach var {encrypt sign format version} {
  489.             set pgp($var,$id) $pgp($var)
  490.         }
  491.         }
  492.     }
  493.     reedit    {
  494.         Exmh_Status " "
  495.             # Rename the orig message back to the draft
  496.         Mhn_RenameOrig $msg
  497.         EditWhatNow $msg prog
  498.     }
  499.     sedit    {
  500.         Exmh_Status " "
  501.         EditWhatNow $msg sedit
  502.     }
  503.     alternate {
  504.         Exmh_Status " "
  505.         EditWhatNow $msg alternate
  506.     }
  507.         spell {
  508.             Exmh_Status " "
  509.             EditWhatNow $msg spell
  510.         }
  511.         mhn {
  512.         # If the orig file exists, move it back to the draft
  513.         Mhn_RenameOrig $msg
  514.         if [info exists path] {
  515.         set env(mhdraft) $path
  516.         } else {
  517.         set env(mhdraft) [Mh_Path $mhProfile(draft-folder) $msg]
  518.         }
  519.         if [catch {exec $editor(mhn) $env(mhdraft)} err] {
  520.         EditDialogMsg $msg "MHN failed: $err"
  521.         } else {
  522.         EditDialogMsg $msg "MHN returned a-o.k."
  523.         }
  524.         }
  525.     abort    {
  526.         if ![info exists path]  {
  527.         catch {Mh_Rmm $mhProfile(draft-folder) $msg}
  528.         Mhn_DeleteOrig $msg
  529.         if {$exmh(folder) == $mhProfile(draft-folder)} {
  530.             # Clean up scan listing
  531.             if [catch {Msg_RemoveById $msg} err] {
  532.             Exmh_Debug Msg_RemoveById $msg $err
  533.             }
  534.         }
  535.         Exmh_Status "Draft $msg aborted" error
  536.         Quote_Cleanup
  537.         Mh_AnnoCleanup $msg
  538.         if $pgp(enabled) {
  539.             # Done with this draft, set PGP defaults for next call
  540.             set id [SeditId $msg]
  541.             foreach var {encrypt sign format version} {
  542.             set pgp($var,$id) $pgp($var)
  543.             }
  544.         }
  545.         }
  546.     }
  547.     dismiss    {
  548.         Exmh_Status "Draft $msg dismissed" normal
  549.         Quote_Cleanup
  550.         Mhn_RenameOrig $msg
  551.         Mh_AnnoCleanup $msg
  552.     }
  553.     whom    {
  554.         Mh_AnnoEnviron $msg
  555.         if [info exists path] {
  556.         catch {Mh_Whom $path} result
  557.         } else {
  558.         catch {Mh_Whom $msg} result
  559.         }
  560.         EditDialogMsg $msg $result
  561.     }
  562.     default    {
  563.         Exmh_Error "Unknown action in Edit_Done"
  564.     }
  565.     }
  566. }
  567.  
  568.